package org.voovan.korla.socket;

import org.voovan.korla.exception.KorlaException;
import org.voovan.korla.handler.Handler;
import org.voovan.korla.message.Msg;
import org.voovan.korla.message.MsgRegister;
import org.voovan.network.ConnectModel;
import org.voovan.network.HeartBeat;
import org.voovan.network.IoHandler;
import org.voovan.network.IoSession;
import org.voovan.network.exception.SendMessageException;
import org.voovan.tools.log.Logger;

import java.util.concurrent.TimeoutException;

import static org.voovan.korla.KorlaStatic.*;

/**
 * Socket 句柄类
 *
 * @author: helyho
 * korla Framework.
 * WebSite: https://github.com/helyho/korla
 * Licence: Apache v2 License
 */
public class SocketHandler implements IoHandler {
    private Handler handler;

    public SocketHandler() {

    }

    public SocketHandler(Handler handler) {
        this.handler = handler;
    }

    public Handler getHandler() {
        return handler;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

    @Override
    public Object onConnect(IoSession ioSession) {
        HeartBeat heartBeat = ioSession.getHeartBeat();
        if (heartBeat == null) {
            //心跳绑定到 Session
            HeartBeat.attachSession(ioSession, PING, PONG);
        }

        return null;
    }

    @Override
    public void onDisconnect(IoSession ioSession) {
        if(handler!=null) {
            handler.disconnect(ioSession);
        } else {
            Logger.info("Disconnect " + ioSession);
        }
    }

    @Override
    public Object onReceive(IoSession ioSession, Object o) {
        if(o instanceof Msg) {
            boolean hasException = false;

            Msg msg = (Msg) o;
            Msg result = null;
            try {
                 //============================ register ============================
                if(msg.isRegMsg()) {
                    MsgRegister msgRegister = (MsgRegister)msg;

                    result = register(ioSession, msgRegister);
                }

                //============================ Receive ============================
                else if(getChannel(ioSession)!=null) {
                    result = receive(ioSession, msg);
                }

                //============================ Unknow ============================
                else {
                    Logger.warn("[Korla] ProviderHandler.connect error channel is null, socket will be close");
                    ioSession.close();
                }
            } catch (Throwable e) {
                hasException = true;
                throw e;
            }

            //1.清理结束消息, 场景是接收作为最后一条消息
            //2.异常清理消息, 避免内存堆积占用 OOM
            if(result == null || hasException) {
                Msg.removeFromCache(msg);
            }

            return result;
        } else {
            ioSession.close();
            throw new KorlaException("Handler.onReceive data is not a Object of Msg.class");
        }

    }

    private Msg register(IoSession ioSession, MsgRegister msgRegister){
        ioSession.setAttribute(CONNECT_STR, true);
        try {
            if(ioSession.socketContext().getConnectModel() == ConnectModel.SERVER) {
                //=====================服务端处理=====================
                    //为会话绑定 Channel,  发送消息时未指定 channel 则自动填写
                    ioSession.setAttribute(CHANNEL_STR, msgRegister.getChannel());

                    //返回注册响应
                    ioSession.syncSend(msgRegister);

                    //触发 onconnect
                    return handler==null ? null : handler.connect(ioSession);
            } else if(ioSession.socketContext().getConnectModel() == ConnectModel.CLIENT){
                //=====================客户端处理=====================
                //为会话绑定 Channel,  发送消息时未指定 channel 则自动填写
                ioSession.setAttribute(CHANNEL_STR, msgRegister.getChannel());
                //触发 onconnect
                return handler==null ? null : handler.connect(ioSession);
            } else {
                ioSession.close();
                return null;
            }
        } catch (SendMessageException e) {
            Logger.error("Send register failed", e);
            ioSession.close();
            return null;
        }
    }

    private Msg receive(IoSession ioSession, Msg msg){
        Msg result = null;

        if(msg.getCallBack() != null) {
            result = msg.getCallBack().execute(msg);
        } else if(handler!=null){
            result = handler.receive(ioSession, msg);
        }

        return result;
    }

    @Override
    public void onSent(IoSession ioSession, Object o) {
        if(o instanceof Msg) {
            Msg msg = (Msg)o;
            if(msg.isForceFlush()) {
                ioSession.flush();
            }
        }
    }

    @Override
    public void onFlush(IoSession ioSession) {

    }

    @Override
    public void onException(IoSession ioSession, Exception e) {
        if(!(e instanceof TimeoutException)) {
            if(handler!=null) {
                handler.exception(ioSession, e);
            } else {
                Logger.error("[Korla] SocketHandler has error", e);
            }
        }
    }

    @Override
    public void onIdle(IoSession ioSession) {
        HeartBeat heartBeat = ioSession.getHeartBeat();
        if (heartBeat != null) {
            heartBeat.beat(ioSession);
        }
    }
}
